package com.wjd.wshow.common.resource.loader;

import com.wjd.wshow.common.resource.Resource;
import com.wjd.wshow.common.resource.ResourceType;
import com.wjd.wshow.config.ResourceConfig;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;

public class ResourceLoader {

    private static Logger logger = LoggerFactory.getLogger(ResourceLoader.class);

    private static int defaultBufSize = 2048;

    private static final ResourceLoader loader = new ResourceLoader();

    public static ResourceLoader getLoader() {
        return loader;
    }

    /**
     * 资源根目录路径
     * @return 根目录路径
     */
    public Path getRootPath() {
        Path rootPath;
        String path = ResourceConfig.getInstance().getRootPath();
        rootPath = Paths.get(path);
        return rootPath;
    }

    /**
     * 获取资源上传的目录
     * @param resourceType 上传资源类型
     * @return 目录路径
     */
    public Path getResourcePath(ResourceType resourceType) {
        Path resourcePath = getRootPath();
        if (resourceType == null) {
            return resourcePath;
        }
        resourcePath = resourcePath.resolve(resourceType.name());
        return resourcePath;
    }

    /**
     * 获取资源的绝对路径
     * @param resource 资源
     * @return 绝对路径
     */
    public Path getAbsPath(Resource resource) {
        if (resource == null) {
            return null;
        }
        Path absPath = resource.getPath();
        if (!absPath.isAbsolute()) {
            Path resourcePath = getLoader().getResourcePath(resource.getType());
            absPath = resourcePath.resolve(absPath);
        }
        return absPath;
    }

    /**
     * 保存资源
     * @param resource 资源
     * @return 保存后的资源
     */
    public Resource storeResource(Resource resource) throws IOException {
        if (resource == null) {
            return null;
        }
        Path path = getLoader().getAbsPath(resource);
        if (Files.notExists(path)) {
            Path parent = path.getParent();
            Files.createDirectories(parent);
            Files.createFile(path);
        }
        InputStream inputStream = resource.getInputStream();
        Files.copy(inputStream, path, StandardCopyOption.REPLACE_EXISTING);
        logger.info("Store resource success: " + path);
        return resource;
    }

    /**
     * 删除资源
     * @param resource 资源
     * @return 不存在或删除成功 true，删除失败 false
     */
    public boolean deleteResource(Resource resource) {
        try {
            Path absPath = getAbsPath(resource);
            boolean exist = Files.exists(absPath);
            if (!exist) {
                logger.warn("Delete resource not exist: " + absPath);
                return true;
            }
            FileUtils.forceDelete(absPath.toFile());
            logger.info("Delete resource success: " + absPath);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 加载资源
     * @param resource 资源
     * @param outputStream 输出流
     */
    public void loadResource(Resource resource, OutputStream outputStream) throws IOException {
        Path path = ResourceLoader.getLoader().getAbsPath(resource);
        if (Files.notExists(path)) {
            throw new IllegalArgumentException("Resource not exist: " + path);
        }
        Files.copy(path, outputStream);
    }

    /**
     * 随机读取资源的数据（按字节）
     *
     * @param resource 资源
     * @param output 输出流
     * @param offset 偏移
     * @param length 长度
     * @throws IOException 资源访问出现问题时抛异常
     * @return 实际复制的长度
     */
    public long randomCopyResource(Resource resource, OutputStream output, long offset, long length) throws IOException {
        if (resource == null || output == null || length <= 0) {
            return 0;
        }

        // 资源通道
        Path path = getAbsPath(resource);
        SeekableByteChannel seekChannel = Files.newByteChannel(path);
        seekChannel.position(offset);

        // 缓冲池
        int bufferCapacity = defaultBufSize;
        ByteBuffer buffer = ByteBuffer.allocate(bufferCapacity);
        long bytesToRead = bufferCapacity;
        if (length < bytesToRead) {
            bytesToRead = (int) length;
        }

        // 读写数据
        long totalRead = 0L;
        int read;
        while (bytesToRead > 0 && -1 != (read = seekChannel.read(buffer))) {
            output.write(buffer.array(), 0, read);
            totalRead += read;
            bytesToRead = (int) Math.min(length - totalRead, bufferCapacity);
            buffer.flip();
        }
        seekChannel.close();
        return totalRead;
    }
}
